home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 4 / QRZ Ham Radio Callsign Database - Volume 4.iso / files / tcpip / amiga / asrc29p.lha / nntpcli.cxx < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-29  |  30.6 KB  |  1,118 lines

  1. /*
  2.  *   Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *   Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *   Permission granted for non-commercial copying and use, provided
  6.  *   this notice is retained.
  7.  *
  8.  *   Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *   Permission granted for non-commercial copying and use, provided
  10.  *   this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  *     July 12, 1992 G1YYH added POST, PROFILE, KILL, MESSAGES subcommands
  32.  */
  33. #include <stdio.h>
  34. #include <exec/types.h>
  35. #include <time.h>
  36. #include <ctype.h>
  37. #include <string.h>  /* for strchr() */
  38. #include "global.h"
  39. #include "timer.h"
  40. #include "cmdparse.h"
  41. #include "commands.h"
  42. #include "socket.h"
  43. #include "usock.h"
  44. #include "netuser.h"
  45. #include "proc.h"
  46. #include "smtp.h"
  47. #include "files.h"
  48. #include "session.h"
  49. #include "dirutil.h"
  50. #include "amiga/stat.h"
  51.  
  52. #define NNTPMAXLEN   512
  53.  
  54. static struct nntpservers {
  55.    struct timer nntpcli_t;
  56.    char *name;
  57.    char *groups;
  58.    int lowtime, hightime;  /* for connect window */
  59.    struct nntpservers *next;
  60. };
  61.  
  62. #define   NULLNNTP   (struct nntpservers *)NULL
  63.  
  64. #define MAXGROUPDIRS 10
  65.  
  66. static struct grouploc {
  67.    char *prefix;        /* e.g. comp, rec, net, talk, alt ... */
  68.    char *directory;     /* directory where these groups should be */
  69.    } groupdirs[MAXGROUPDIRS] = { NULL, NULL };
  70.  
  71. struct nntpservers *Nntpservers = NULLNNTP;
  72. static char *Nntpgroups = NULLCHAR;
  73. static unsigned short nntptrace = 1;
  74.  
  75. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  76.  
  77. static void nntptick __ARGS((void *tp));
  78. static void nntp_job __ARGS((int i1,void *tp,void *v1));
  79. static int gettxt __ARGS((int s,FILE *fp));
  80. static int getreply __ARGS((int s));
  81. static int getarticle __ARGS((int s,char *msgid));
  82. static int dogroups __ARGS((int argc,char *argv[],void *p));
  83. static int doadds __ARGS((int argc,char *argv[],void *p));
  84. static int dodrops __ARGS((int argc,char *argv[],void *p));
  85. static int dokicks __ARGS((int argc,char *argv[],void *p));
  86. static int dolists __ARGS((int argc,char *argv[],void *p));
  87. static int donntrace __ARGS((int argc,char *argv[],void *p));
  88. static int dondir __ARGS((int argc,char *argv[],void *p));
  89.  
  90. static int donnprofile __ARGS((int argc,char *argv[],void *p));
  91. static int donnfull __ARGS((int argc,char *argv[],void *p));
  92. static int donnhost __ARGS((int argc,char *argv[],void *p));
  93. static int donnorgan __ARGS((int argc,char *argv[],void *p));
  94. static int donnreply __ARGS((int argc,char *argv[],void *p));
  95. static int donnsig __ARGS((int argc,char *argv[],void *p));
  96. static int donnuser __ARGS((int argc,char *argv[],void *p));
  97. static int donnpost __ARGS((int argc,char *argv[],void *p));
  98. static int check_blank __ARGS((char *bp));
  99. static int donntplist __ARGS((int argc,char *argv[],void *p));
  100. static int donntpkill __ARGS((int argc,char *argv[],void *p));
  101.  
  102. static char *Puser     = NULLCHAR;
  103. static char *Preply    = NULLCHAR;
  104. static char *Psig      = NULLCHAR;
  105. static char *Porgan    = NULLCHAR;
  106. static char *Pfullname = NULLCHAR;
  107. static char *Host      = NULLCHAR;
  108.  
  109. /* Tracing levels:
  110.    0 - no tracing
  111.    1 - serious errors reported
  112.    2 - transient errors reported
  113.    3 - session progress reported
  114.    4 - actual received articles displayed
  115.  */
  116.  
  117. static struct cmds Nntpcmds[] = {
  118.    "addserver",   doadds,      0, 3, "nntp addserver <nntpserver> <interval>",
  119.    "directory",   dondir,      0, 0, NULLCHAR,
  120.    "dropserver",  dodrops,     0, 2, "nntp dropserver <nntpserver>",
  121.    "groups",      dogroups,    0, 0, NULLCHAR,
  122.    "kick",        dokicks,     0, 2, "nntp kick <nntpserver>",
  123.    "kill",        donntpkill,  0, 2, "kill <jobnumber>",
  124.    "listservers", dolists,     0, 0, NULLCHAR,
  125.    "messages",    donntplist,  0, 0, NULLCHAR,
  126.    "profile",     donnprofile, 0, 0, NULLCHAR,
  127.    "post",        donnpost,    2024, 0, NULLCHAR,
  128.    "trace",       donntrace,   0, 0, NULLCHAR,
  129.    NULLCHAR,
  130. };
  131.  
  132. int donntp(argc,argv,p)
  133. int argc;
  134. char *argv[];
  135. void *p;
  136. {
  137.    if(argc == 1)
  138.       return donntplist(argc,argv,p);
  139.    return subcmd(Nntpcmds,argc,argv,p);
  140. }
  141.  
  142. static int doadds(argc,argv,p)
  143. int argc;
  144. char *argv[];
  145. void *p;
  146. {
  147.    struct nntpservers *np;
  148.    for(np = Nntpservers; np != NULLNNTP; np = np->next)
  149.       if(stricmp(np->name,argv[1]) == 0)
  150.          break;
  151.    if (np == NULLNNTP) {
  152.       np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  153.       np->name = strdup(argv[1]);
  154.       np->next = Nntpservers;
  155.       Nntpservers = np;
  156.       np->groups = NULLCHAR;
  157.       np->lowtime = np->hightime = -1;
  158.       np->nntpcli_t.func = nntptick;   /* what to call on timeout */
  159.       np->nntpcli_t.arg = (void *)np;
  160.    }
  161.    if (argc > 3) {
  162.       int i;
  163.       if (np->groups == NULLCHAR) {
  164.          np->groups = mallocw(NNTPMAXLEN);
  165.          *np->groups = '\0';
  166.       }
  167.       for (i = 3; i < argc; ++i) {
  168.          if (isdigit(*argv[i])) {
  169.             int lh, ll, hh, hl;
  170.             sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  171.             np->lowtime = lh * 100 + ll;
  172.             np->hightime = hh * 100 + hl;
  173.          } else if ((strlen(np->groups)+strlen(argv[i])+2) >= NNTPMAXLEN)
  174.             tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  175.          else {  /* it's a group, and it fits... add it to list */
  176.             if (*np->groups != '\0')
  177.                strcat(np->groups, ",");
  178.             strcat(np->groups, argv[i]);
  179.          }
  180.       }
  181.    }
  182.    /* set timer duration */
  183.    np->nntpcli_t.start = atol(argv[2])*(1000/MSPTICK);
  184.    start_timer(&np->nntpcli_t);      /* and fire it up */
  185.    return 0;
  186. }
  187.  
  188. static int dodrops(argc,argv,p)
  189. int argc;
  190. char *argv[];
  191. void *p;
  192. {
  193.    struct nntpservers *np, *npprev = NULLNNTP;
  194.    for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  195.       if(stricmp(np->name,argv[1]) == 0) {
  196.          stop_timer(&np->nntpcli_t);
  197.          free(np->name);
  198.          if (np->groups)
  199.             free(np->groups);
  200.          if(npprev != NULLNNTP)
  201.             npprev->next = np->next;
  202.          else
  203.             Nntpservers = np->next;
  204.          free((char *)np);
  205.          return 0;
  206.    }
  207.    tprintf("No such server enabled.\n");
  208.    return 0;
  209. }
  210.  
  211. static int dolists(argc,argv,p)
  212. int argc;
  213. char *argv[];
  214. void *p;
  215. {
  216.    struct nntpservers *np;
  217.    for(np = Nntpservers; np != NULLNNTP; np = np->next) {
  218.       char tbuf[80];
  219.       if (np->lowtime != -1 && np->hightime != -1)
  220.          sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  221.       else
  222.          tbuf[0] = '\0';
  223.       tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  224.          read_timer(&np->nntpcli_t) * MSPTICK/1000,
  225.          dur_timer(&np->nntpcli_t) * MSPTICK/1000,
  226.          tbuf, np->groups ? np->groups : "");
  227.    }
  228.    return 0;
  229. }
  230.  
  231. static int donntrace(argc, argv, p)
  232. int argc;
  233. char *argv[];
  234. void *p;
  235. {
  236.    return setshort(&nntptrace,"NNTP tracing",argc,argv);
  237. }
  238.    
  239. static char *News_spool = NULL;
  240. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  241.  
  242. static int dondir(argc, argv, p)
  243. int argc;
  244. char *argv[];
  245. void *p;
  246. {
  247.    if (argc < 2) {
  248.       int i;
  249.       tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  250.       tprintf("control: %s\n", Newsdir);
  251.       for (i = 0; i < MAXGROUPDIRS; ++i)
  252.          if (groupdirs[i].prefix)
  253.             tprintf("%-10.10s %s\n", groupdirs[i].prefix, groupdirs[i].directory);
  254.    } else {
  255.       char *p;
  256.       if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  257.          int i;
  258.          *p++ = '\0';
  259.          for (i = 0; i < MAXGROUPDIRS; ++i)
  260.             if (groupdirs[i].prefix)
  261.                if (!strnicmp(groupdirs[i].prefix, argv[1], strlen(argv[1]))) {
  262.                   if (groupdirs[i].directory) {
  263.                      free(groupdirs[i].directory);
  264.                      groupdirs[i].directory = NULLCHAR;
  265.                      }
  266.                   if (*p == '\0') {
  267.                      free(groupdirs[i].prefix);
  268.                      groupdirs[i].prefix = NULLCHAR;
  269.                   } else
  270.                      groupdirs[i].directory = strdup(p);
  271.                   return 0;
  272.                }
  273.          if (*p == '\0')  /* trashing a group that's not there */
  274.             return 0;
  275.          for (i = 0; i < MAXGROUPDIRS; ++i){
  276.             if (groupdirs[i].prefix == NULLCHAR) {
  277.                groupdirs[i].prefix = strdup(argv[1]);
  278.                if (groupdirs[i].directory) {
  279.                   free(groupdirs[i].directory);
  280.                   groupdirs[i].directory = NULL;
  281.                }
  282.                groupdirs[i].directory = strdup(p);
  283.                return 0;
  284.             }
  285.          }
  286.          tprintf("Directory table full\n");
  287.       }
  288.       else {  /* no '=', so just set default */
  289.          if (News_spool)
  290.             free(News_spool);
  291.          News_spool = strdup(argv[1]);
  292.       }
  293.       if (argc > 2) {  /* they specified a newsdir as well */
  294.          if (np_all)
  295.             free(Newsdir);
  296.          Newsdir = strdup(argv[2]);
  297.          np_all = 1;
  298.       }
  299.    }
  300.    return 0;
  301. }
  302.    
  303. static int dokicks(argc,argv,p)
  304. int argc;
  305. char *argv[];
  306. void *p;
  307. {
  308.    struct nntpservers *np;
  309.    for(np = Nntpservers; np != NULLNNTP; np = np->next)
  310.       if(stricmp(np->name,argv[1]) == 0) {
  311.          /* If the timer is not running, the timeout function has
  312.          * already been called and we don't want to call it again.
  313.          */
  314.          if(run_timer(&np->nntpcli_t)) {
  315.             stop_timer(&np->nntpcli_t);
  316.             nntptick((void *)np);
  317.          }
  318.          return 0;
  319.    }
  320.    tprintf("No such server enabled.\n");
  321.    return 0;
  322. }
  323.  
  324. static int dogroups(argc,argv,p)
  325. int argc;
  326. char *argv[];
  327. void *p;
  328. {
  329.    int i;
  330.    if(argc < 2) {
  331.       if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  332.          tprintf("All groups are currently enabled.\n");
  333.       else
  334.          tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  335.       return 0;
  336.    }
  337.    if(Nntpgroups == NULLCHAR)
  338.       Nntpgroups = mallocw(NNTPMAXLEN);
  339.    *Nntpgroups = '\0';
  340.    for(i=1; i < argc; ++i) {
  341.       if(i > 1)
  342.          strcat(Nntpgroups,",");
  343.       strcat(Nntpgroups,argv[i]);
  344.    }
  345.    return 0;
  346. }
  347.  
  348. /* This is the routine that gets called every so often to connect to
  349.  * NNTP servers.
  350.  */
  351. static void nntptick(tp)
  352. void *tp;
  353. {
  354.    newproc("NNTP client", 3072, nntp_job, 0, tp, NULL);
  355. }
  356.  
  357. static void nntp_job(i1,tp,v1)
  358. int i1;
  359. void *tp, *v1;
  360. {
  361.    FILE *fp, *tmpf, *wfile, *tfile;
  362.    int s, i;
  363. /*   long pos; */
  364.    struct tm *ltm;
  365.    time_t t;
  366.    int now;
  367.    struct nntpservers *np = (struct nntpservers *) tp;
  368.    struct sockaddr_in fsocket;
  369.    char tbuf[NNTPMAXLEN], buf[NNTPMAXLEN], *cp, *cp1,
  370.       *lastdate = NULLCHAR, wfilename[13], nnfix[9],
  371.       wtmp[LINELEN], ttmp[LINELEN], wgroup[80], wmsgid[80];
  372.  
  373.    if (nntptrace >= 2)
  374.       mainlog(-1,"NNTP Checking NewsGroups on \"%s\"",np->name);
  375.    if(availmem() < Memthresh){
  376.       if (nntptrace >= 2)
  377.          mainlog(-1,"NNTP daemon quit -- low memory");
  378.       /* Memory is tight, don't do anything */
  379.       start_timer(&np->nntpcli_t);
  380.       return;
  381.    }
  382.  
  383.    time(&t);   /* more portable than gettime() */
  384.    ltm = localtime(&t);
  385.    now = ltm->tm_hour * 100 + ltm->tm_min;
  386.    if (np->lowtime < np->hightime) {  /* doesn't cross midnight */
  387.       if (now < np->lowtime || now >= np->hightime) {
  388.          if (nntptrace >= 3)
  389.             mainlog(-1,"NNTP window to '%s' not open", np->name);
  390.          start_timer(&np->nntpcli_t);
  391.          return;
  392.       }
  393.    } else {
  394.       if (now < np->lowtime && now >= np->hightime) {
  395.          if (nntptrace >= 3)
  396.             mainlog(-1,"NNTP window to '%s' not open", np->name);
  397.          start_timer(&np->nntpcli_t);
  398.          return;
  399.       }
  400.    }
  401.  
  402.    fsocket.sin_addr.s_addr = resolve(np->name);
  403.    if(fsocket.sin_addr.s_addr == 0) {  /* No IP address found */
  404.       if (nntptrace >= 2)
  405.          mainlog(-1,"NNTP can't resolve host \"%s\"", np->name);
  406.       /* Try again later */
  407.       start_timer(&np->nntpcli_t);
  408.       return;
  409.    }
  410.    fsocket.sin_family = AF_INET;
  411.    fsocket.sin_port = IPPORT_NNTP; 
  412.  
  413.    s = socket(AF_INET,SOCK_STREAM,0);
  414.    /* sockmode(s,SOCK_ASCII); */
  415.    if(connect(s,(char *)&fsocket,SOCKSIZE) == -1){
  416.       cp = sockerr(s);
  417.       mainlog(-1,"NNTP %s Connect failed: %s",psocket(&fsocket),
  418.          cp != NULLCHAR ? cp : "");
  419.       goto quit;
  420.    }
  421.    /* Eat the banner */
  422.    i = getreply(s);
  423.    if(i >= 400) {
  424.       mainlog(-1,"NNTP %s bad reply on banner (response was %d)",psocket(&fsocket),i);
  425.       goto quit;
  426.    }
  427.  
  428. if(i == 201) {
  429.    if (nntptrace >= 1)
  430.       mainlog(-1,"NNTP - No posting allowed on \"%s\"", np->name);
  431. } else {
  432.    for(filedir(Newsqueue,0,wfilename); wfilename[0] != '\0';
  433.       filedir(Newsqueue,1,wfilename)) {
  434.  
  435.       cp = wfilename;
  436.       cp1 = nnfix;
  437.       while (*cp && *cp != '.')
  438.          *cp1++ = *cp++;
  439.       *cp1 = '\0';
  440.  
  441.       sprintf(wtmp,"%s/%s",Newsdir,wfilename);
  442.       if ((wfile = fopen(wtmp,READ_TEXT)) == NULLFILE) 
  443.          continue;
  444.       (void) fgets(wgroup,LINELEN,wfile);      /* read target group */
  445.       rip(wgroup);
  446.       (void) fgets(wmsgid,LINELEN,wfile);      /* read messageid */
  447.       rip(wmsgid);
  448.       fclose(wfile);
  449.  
  450.       if (nntptrace >= 3)
  451.          mainlog(-1,"==>GROUP %s", wgroup);
  452.       usprintf(s,"GROUP %s\n", wgroup);
  453.       i = getreply(s);
  454.       if(i == 411) {
  455.          if (nntptrace >= 1)
  456.             mainlog(-1,"NNTP No Such Newsgroup '%s'",wgroup);
  457.          continue;
  458.       }
  459.       if(i == 211) {
  460.          if (nntptrace >= 3)
  461.             mainlog(-1,"==>IHAVE %s", wmsgid);
  462.          usprintf(s,"IHAVE %s\n", wmsgid);
  463.          i = getreply(s);
  464.          if(i == 335) {
  465.             /* Send It */
  466.             sprintf(ttmp,"%s/%s.txt", Newsdir, nnfix);
  467.             if((tfile = fopen(ttmp, READ_TEXT)) != NULLFILE) {
  468.                while(fgets(ttmp, LINELEN, tfile) != NULLCHAR)
  469.                   usprintf(s,ttmp);
  470.                fclose(tfile);
  471.             }
  472.             i = getreply(s);
  473.             if(i == 235)
  474.                remove(wtmp);
  475.          }
  476.          if(i == 436)
  477.             if (nntptrace >= 1)
  478.                mainlog(-1,"NNTP '%s' Transfer Failed - Try later", wmsgid);
  479.          if(i == 435) {
  480.             if (nntptrace >= 1)
  481.                mainlog(-1,"NNTP Article '%s' Not wanted", wmsgid);
  482.             remove(wtmp);
  483.          }
  484.          if(i == 437) {
  485.             if (nntptrace >= 1)
  486.                mainlog(-1,"NNTP Article '%s' Rejected", wmsgid);
  487.             remove(wtmp);
  488.          }
  489.       }
  490.    }
  491. }
  492.  
  493.    if (mlock(Newsdir, "nntp")) {
  494.       if (nntptrace >= 2)
  495.          mainlog(-1,"NNTP %s Connect failed: cannot lock nntp.dat",
  496.                     psocket(&fsocket));
  497.       goto quit;
  498.    }
  499.    sprintf(buf,"%s/nntp.dat",Newsdir);
  500.    if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  501.       mainlog(-1,"NNTP %s Connect failed: Cannot open %s",
  502.                  psocket(&fsocket), buf);
  503.       rmlock(Newsdir, "nntp");
  504.       goto quit;
  505.    }
  506.    rewind(fp);
  507. /*   for(pos=0L; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;pos=ftell(fp)) { */
  508.    for(; fgets(buf,NNTPMAXLEN,fp) != NULLCHAR;) {
  509.       if((cp = strchr(buf,' ')) == NULLCHAR)
  510.          continue;   /* something wrong with this line, skip it */
  511.       *cp = '\0';
  512.       if(stricmp(buf,np->name) == 0) {
  513.          rip(cp+1);
  514.          lastdate = strdup(cp+1);
  515.          break;
  516.       }
  517.    }
  518.    fclose(fp);
  519.    rmlock(Newsdir, "nntp");
  520.  
  521.    if(lastdate == NULLCHAR)
  522.       lastdate = strdup("700101 000000");
  523.    /* snapshot the time for use later in re-writing nntp.dat */
  524.    time(&t);
  525.    ltm = localtime(&t);
  526.             
  527.    /* Get a list of new message-id's */
  528.    if (np->groups) {
  529.       if (nntptrace >= 3)
  530.          mainlog(-1,"==>NEWNEWS %s %s", np->groups, lastdate);
  531.       usprintf(s,"NEWNEWS %s %s\n", np->groups, lastdate);
  532.    } else {
  533.       if (nntptrace >= 3)
  534.          mainlog(-1,"==>NEWNEWS %s %s",
  535.                     Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  536.       usprintf(s,"NEWNEWS %s %s\n",Nntpgroups != NULLCHAR ? Nntpgroups : "*", lastdate);
  537.    }
  538.    free(lastdate);
  539.    /* Get the response */
  540.    if((i = getreply(s)) != 230) { /* protocol error */
  541.       mainlog(-1,"NNTP %s protocol error (response was %d)",
  542.                  psocket(&fsocket),i);
  543.       goto quit;
  544.    }
  545.    if((tmpf = tmpfile()) == NULLFILE) {
  546.       if (nntptrace >= 1)
  547.          mainlog(-1,"NNTP %s Cannot open temp file", psocket(&fsocket));
  548.       goto quit;
  549.    }
  550.    if(gettxt(s,tmpf) == -1) {
  551.       mainlog(-1,"NNTP %s giving up: gettxt() failure",psocket(&fsocket));
  552.       fclose(tmpf);
  553.       goto quit;
  554.    }
  555.  
  556.    /* Open the history file */
  557.    if (mlock(Newsdir, "history")) {
  558.       if (nntptrace >= 1)
  559.          mainlog(-1,"NNTP %s giving up: couldn't lock history file",
  560.                     psocket(&fsocket));
  561.       fclose(tmpf);
  562.       goto quit;
  563.    }
  564.    sprintf(buf,"%s/history",Newsdir);
  565.    if((fp = fopen(buf,APPEND_TEXT)) == NULLFILE) {
  566.       mainlog(-1,"NNTP %s Connect failed: Cannot open %s",
  567.                  psocket(&fsocket), buf);
  568.       fclose(tmpf);
  569.       goto quit;
  570.    }
  571.    /* search through the history file for matching message id's */
  572.    rewind(tmpf);
  573.    while(fgets(tbuf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  574.       i = 0;
  575.       rewind(fp);
  576.       while(fgets(buf,NNTPMAXLEN,fp) != NULLCHAR) {
  577.          if(stricmp(buf,tbuf) == 0) {
  578.             i = 1;
  579.             break;
  580.          }
  581.          pwait(NULL);
  582.       }
  583.       if(i == 0) {      /* not found, get the article */
  584.          if(getarticle(s,tbuf) == -1) {
  585.             mainlog(-1,"NNTP %s Giving up: could not get article",
  586.                        psocket(&fsocket));
  587.             fclose(fp);
  588.             rmlock(Newsdir, "history");
  589.             fclose(tmpf);
  590.             goto quit;
  591.          }
  592.          fprintf(fp,"%s",tbuf); /* add the new message id */
  593.       }
  594.    }
  595.    fclose(fp);
  596.    rmlock(Newsdir, "history");
  597.    fclose(tmpf);
  598.    if (nntptrace >= 3)
  599.       mainlog(-1,"==>QUIT");
  600.    usprintf(s,"QUIT\n");
  601.    /* Eat the response */
  602.    getreply(s);
  603.    /* NOW, update the nntp.dat file */
  604.    if (mlock(Newsdir, "nntp")) {
  605.       if (nntptrace >= 2)
  606.          mainlog(-1,"NNTP %s Could not lock nntp.dat for update", 
  607.                     psocket(&fsocket));
  608.       goto quit;
  609.    }
  610.    sprintf(buf,"%s/nntp.dat",Newsdir);
  611.    fp = fopen(buf,READ_TEXT);
  612.    sprintf(buf, "%s/nntp.tmp",Newsdir);
  613.    if ((tmpf = fopen(buf, WRITE_TEXT)) == NULLFILE)
  614.       if (nntptrace >= 1)
  615.          mainlog(-1,"NNTP %s Cannot create temp file '%s'",
  616.                     psocket(&fsocket), buf);
  617.    if (fp == NULLFILE || tmpf == NULLFILE) {
  618.       mainlog(-1,"NNTP %s Could not update %s", psocket(&fsocket), buf);
  619.       if (fp)
  620.          fclose(fp);
  621.       if (tmpf)
  622.          fclose(tmpf);
  623.       rmlock(Newsdir, "nntp");
  624.       goto quit;
  625.    }
  626.    while (fgets(tbuf, sizeof(tbuf), fp))
  627.       if (strnicmp(tbuf, np->name, strlen(np->name)))
  628.          fputs(tbuf, tmpf);
  629.    fprintf(tmpf,"%s %02d%02d%02d %02d%02d%02d\n",np->name,ltm->tm_year%100,ltm->tm_mon+1,
  630.       ltm->tm_mday,ltm->tm_hour,ltm->tm_min,ltm->tm_sec);
  631.    fclose(fp);
  632.    fclose(tmpf);
  633.    sprintf(buf, "%s/nntp.dat", Newsdir);
  634.    sprintf(tbuf, "%s/nntp.tmp", Newsdir);
  635.    remove(buf);
  636.    rename(tbuf, buf);
  637.    rmlock(Newsdir, "nntp");
  638. quit:
  639.    if (nntptrace >= 2)
  640.       mainlog(-1,"NNTP Finished with \"%s\"", np->name);
  641.    close_s(s);
  642.    /* Restart timer */
  643.    start_timer(&np->nntpcli_t);
  644.    return;
  645. }
  646.  
  647. static int gettxt(s,fp)
  648. int s;
  649. FILE *fp;
  650. {
  651.    char buf[NNTPMAXLEN];
  652.    int nlines;
  653.    for (nlines = 0; recvline(s,buf,NNTPMAXLEN) != -1; ++nlines) {
  654.       if (nntptrace >= 4)
  655.          mainlog(-1,"<==%s", buf);
  656.       if(strcmp(buf,".\n") == 0) {
  657.          if (nntptrace >= 3)
  658.             mainlog(-1,"NNTP received %d lines", nlines);
  659.          return 0;
  660.          }
  661.       /* check for escaped '.' characters */
  662.       if(strcmp(buf,"..\n") == 0)
  663.          fputs(".\n",fp);
  664.       else
  665.          fputs(buf,fp);
  666.    }
  667.    if (nntptrace >= 1)
  668.       mainlog(-1,"NNTP receive error after %d lines", nlines);
  669.    return -1;
  670. }
  671.  
  672. static int getreply(s)
  673. int s;
  674. {
  675.    char buf[NNTPMAXLEN];
  676.    int response;
  677.    while(recvline(s,buf,NNTPMAXLEN) != -1) {
  678.       /* skip informative messages and blank lines */
  679.       if(buf[0] == '\0' || buf[0] == '1' || buf[0] == '5')
  680.          continue;
  681.       sscanf(buf,"%d",&response);
  682.       if (nntptrace >= 3)
  683.          mainlog(-1,"<==%s", buf);
  684.       return response;
  685.    }
  686.    if (nntptrace >= 3)
  687.       mainlog(-1,"==No response");
  688.    return -1;
  689. }
  690.  
  691. static int getarticle(s,msgid)
  692. int s;
  693. char *msgid;
  694. {
  695.    char buf[NNTPMAXLEN], froml[NNTPMAXLEN], newgl[NNTPMAXLEN];
  696.    FILE *fp, *tmpf;
  697.    int r;
  698.    char *cp;
  699.  
  700.    if (nntptrace >= 3)
  701.       mainlog(-1,"==>ARTICLE %s", msgid);
  702.    usprintf(s,"ARTICLE %s", msgid);
  703.    r = getreply(s);
  704.    if(r == -1 || r >= 500)
  705.       return -1;
  706.    if(r >= 400)
  707.       return 0;
  708.    if((tmpf = tmpfile()) == NULLFILE) {
  709.       if (nntptrace >= 1)
  710.          mainlog(-1,"NNTP Cannot open temp file for article");
  711.       return -1;
  712.    }
  713.    if(gettxt(s,tmpf) == -1) {
  714.       fclose(tmpf);
  715.       return -1;
  716.    }
  717.    /* convert the article into mail format */
  718.    rewind(tmpf);
  719.    froml[0] = '\0';
  720.    newgl[0] = '\0';
  721.    while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  722.       if(strncmp(buf,"From: ",6) == 0) {
  723.          time_t t;
  724.          rip(&buf[6]);
  725.          time(&t);
  726.          sprintf(froml,"From %s %s",&buf[6], ctime(&t));
  727.          if(newgl[0] != '\0')
  728.             break;
  729.       }
  730.       if(strncmp(buf,"Newsgroups: ",12) == 0) {
  731.          strcpy(newgl,&buf[12]);
  732.          if(froml[0] != '\0')
  733.             break;
  734.       }
  735.       /* invalid article - missing 'From:' line or 'Newsgroups:' line */
  736.       if(strcmp(buf,"\n") == 0 && (froml[0] == '\0' || newgl[0] == '\0')) {
  737. /*         fclose(fp); */
  738.          fclose(tmpf);
  739.          return 0;
  740.       }
  741.    }
  742.    sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  743.    for(cp=newgl;;++cp) {
  744.       if(*cp == '.') {
  745.          mkdir(buf); /* create a subdirectory, if necessary */
  746.          strcat(buf,"/");
  747.          continue;
  748.       }
  749.       if(*cp == ',' || *cp == '\n') {
  750.          char tempdir[80], prefix[20], *p;
  751.          strcpy(tempdir, buf);
  752.          if ((p = strrchr(tempdir, '/')) != NULLCHAR) {
  753.             *p++ = '\0';
  754.             strcpy(prefix, p);
  755.          }
  756.          if (mlock(tempdir, prefix)) {
  757.             if (nntptrace >= 2)
  758.                mainlog(-1,"NNTP group '%s' is locked", buf);
  759.             return -1;
  760.          }
  761.          strcat(buf,".txt");
  762.          /* open the mail file */
  763.          if (nntptrace >= 2)
  764.             mainlog(-1,"Writing article to '%s'", buf);
  765.          if((fp = fopen(buf,APPEND_TEXT)) != NULLFILE) {
  766.             fputs(froml,fp);
  767.             rewind(tmpf);
  768.             while(fgets(buf,NNTPMAXLEN,tmpf) != NULLCHAR) {
  769.                /* for UNIX mail compatiblity */
  770.                if(strncmp(buf,"From ",5) == 0)
  771.                   putc('>',fp);
  772.                fputs(buf,fp);
  773.             }
  774.             putc('\n',fp);
  775.             fclose(fp);
  776.          }
  777.          rmlock(tempdir, prefix);
  778.          if (*cp == '\n') 
  779.             break;
  780.          else
  781.             sprintf(buf,"%s/",News_spool ? News_spool : Mailspool);
  782.          continue;
  783.       }
  784.       buf[strlen(buf)+1] = '\0';
  785.       buf[strlen(buf)] = strchr(validchars, tolower(*cp)) ? *cp : '_';
  786.    }
  787.    fclose(tmpf);
  788.    return 0;
  789. }
  790.  
  791. /* -------------------- Profile subcmds -------------------- */
  792.  
  793. static int donnuser(argc,argv,p)
  794. int argc;
  795. char *argv[];
  796. void *p;
  797. {
  798.    if(argc < 2)
  799.       tprintf("%s\n",Puser);
  800.    else {
  801.       free(Puser);
  802.       Puser = strdup(argv[1]);
  803.    }
  804.    return 0;
  805. }
  806.  
  807. static int donnsig(argc,argv,p)
  808. int argc;
  809. char *argv[];
  810. void *p;
  811. {
  812.    if(argc < 2)
  813.       tprintf("%s\n",Psig);
  814.    else {
  815.       if(access(argv[1],0) == 0) {
  816.          free(Psig);
  817.          Psig = strdup(argv[1]);
  818.       } else {
  819.          tputs("No such signature file\n");
  820.          return -1;
  821.       }
  822.    }
  823.    return 0;
  824. }
  825.  
  826. static int donnfull(argc,argv,p)
  827. int argc;
  828. char *argv[];
  829. void *p;
  830. {
  831.    if(argc < 2)
  832.       tprintf("%s\n",Pfullname);
  833.    else {
  834.       free(Pfullname);
  835.       Pfullname = strdup(argv[1]);
  836.    }
  837.    return 0;
  838. }
  839.  
  840. static int donnhost(argc,argv,p)
  841. int argc;
  842. char *argv[];
  843. void *p;
  844. {
  845.    if(argc < 2)
  846.       tprintf("%s\n",Host);
  847.    else {
  848.       free(Host);
  849.       Host = strdup(argv[1]);
  850.    }
  851.    return 0;
  852. }
  853.  
  854. static int donnorgan(argc,argv,p)
  855. int argc;
  856. char *argv[];
  857. void *p;
  858. {
  859.    if(argc < 2)
  860.       tprintf("%s\n",Porgan);
  861.    else {
  862.       free(Porgan);
  863.       Porgan = strdup(argv[1]);
  864.    }
  865.    return 0;
  866. }
  867.  
  868. static int donnreply(argc,argv,p)
  869. int argc;
  870. char *argv[];
  871. void *p;
  872. {
  873.    if(argc < 2)
  874.       tprintf("%s\n",Preply);
  875.    else {
  876.       free(Preply);
  877.       Preply = strdup(argv[1]);
  878.    }
  879.    return 0;
  880. }
  881.  
  882. struct cmds Prof[] = {
  883.    "fullname",   donnfull,    0, 0, NULLCHAR,
  884.    "host",       donnhost,    0, 0, NULLCHAR,
  885.    "organ",      donnorgan,   0, 0, NULLCHAR,
  886.    "reply",      donnreply,   0, 0, NULLCHAR,
  887.    "sig",        donnsig,     0, 0, NULLCHAR,
  888.    "user",       donnuser,    0, 0, NULLCHAR,
  889.    NULLCHAR,
  890. };
  891.  
  892. /* subcmd parser */
  893. static int donnprofile(argc,argv,p)
  894. int argc;
  895. char *argv[];
  896. void *p;
  897. {
  898.    if(argc == 1) {
  899.       if (Host == NULLCHAR) {
  900.          Host = strdup(Hostname);
  901.       }
  902.  
  903.       if (Host != NULLCHAR)
  904.          tprintf("Hostname     : %s\n",Host);
  905.       if (Pfullname != NULLCHAR)
  906.          tprintf("Full Name    : %s\n",Pfullname);
  907.       if (Porgan != NULLCHAR)
  908.          tprintf("Organisation : %s\n",Porgan);
  909.       if (Preply != NULLCHAR)
  910.          tprintf("Reply-To     : %s\n",Preply);
  911.       if (Psig != NULLCHAR)
  912.          tprintf("Signature    : %s\n",Psig);
  913.       if (Puser != NULLCHAR)
  914.          tprintf("User Name    : %s\n",Puser);
  915.  
  916.       return 0;
  917.    }
  918.    return subcmd(Prof,argc,argv,p);
  919. }
  920.  
  921. /* manually entering new news
  922.  * returncode: -1 if error; 0 success */
  923. static int donnpost(argc,argv,p)
  924. int argc;
  925. char *argv[];
  926. void *p;
  927. {
  928.    struct session *sp;
  929.    char buf[NNTPMAXLEN], pbuf[80], qbuf[80];
  930.    int id;
  931.    long currtime;
  932.    FILE *idf, *ufp, *fp, *fq;
  933.  
  934.    if((sp = newsession("Post",POST)) == NULLSESSION) {
  935.       tprintf("Too many sessions\n");
  936.       freeargs(argc,argv);
  937.       return 1;
  938.    }
  939.  
  940.    for (;;) {
  941.       id = get_msgid();
  942.  
  943.       sprintf(pbuf,"%s/%d.txt",Newsdir,id);
  944.       fp = fopen(pbuf,WRITE_TEXT);
  945.       sprintf(qbuf,"%s/%d.wrk",Newsdir,id);
  946.       fq = fopen(qbuf,WRITE_TEXT);
  947.  
  948.       if (Puser == NULLCHAR) {
  949.          tprintf("User name? ");
  950.          recvline(sp->input,buf,NNTPMAXLEN);
  951.          rip(buf);
  952.          if (check_blank(buf)) {
  953.             fclose(fp);
  954.             fclose(fq);
  955.             remove(pbuf);
  956.             remove(qbuf);
  957.             goto done;
  958.          }
  959.          Puser = strdup(buf);
  960.       }
  961.  
  962.       fprintf(fp,"Path: %s\n",Host);
  963.       fprintf(fp,"From: %s@%s",Puser,Hostname);
  964.  
  965.       if (Pfullname != NULLCHAR)
  966.          fprintf(fp," (%s )",Pfullname);
  967.       fprintf(fp,"\n");
  968.  
  969.       tprintf("Newsgroup? ");
  970.       recvline(sp->input,buf,NNTPMAXLEN);
  971.       rip(buf);
  972.       if (check_blank(buf)) {
  973.          fclose(fp);
  974.          fclose(fq);
  975.          remove(pbuf);
  976.          remove(qbuf);
  977.          goto done;
  978.       }
  979.       fprintf(fp,"Newsgroups: %s\n",buf);
  980.       fprintf(fq,"%s\n",buf);
  981.  
  982.       tprintf("Subject? ");
  983.       recvline(sp->input,buf,NNTPMAXLEN);
  984.       rip(buf);
  985.       if (!check_blank(buf))
  986.          fprintf(fp,"Subject: %s\n",buf);
  987.       fprintf(fp,"Message-Id: <%d@%s>\n",id,Hostname);
  988.       fprintf(fq,"<%d@%s>\n",id,Hostname);
  989.       time(&currtime);
  990.       fprintf(fp,"Date: %s",ptime(&currtime));
  991.       fprintf(fp,"Sender: NNTP@%s\n",Hostname);
  992.  
  993.       if (Preply != NULLCHAR)
  994.          fprintf(fp,"Reply-To: %s\n",Preply);
  995.  
  996.       if (Porgan != NULLCHAR)
  997.          fprintf(fp,"Organization: %s\n",Porgan);
  998.  
  999.       fprintf(fp, "Comment: AmigaNOS v%s\n", Version);
  1000.  
  1001.       fprintf(fp,"\n");
  1002.       tputs("Enter message - end with . or /EX ('.r' or '.u' to upload)\n");
  1003.  
  1004.       for (;;) {
  1005.          recvline(sp->input,buf,NNTPMAXLEN);
  1006.          if(strcmp(buf,".u\n") == 0 || strcmp(buf,".r\n") == 0) {
  1007.             tputs("Filename? ");
  1008.             recvline(sp->input, buf, LINELEN);
  1009.             rip(buf);
  1010.             if((ufp = fopen(buf, READ_TEXT)) != NULLFILE) {
  1011.                while(fgets(buf, NNTPMAXLEN, ufp) != NULL)
  1012.                   fputs(buf, fp);
  1013.                fclose(ufp);
  1014.             }
  1015.             tputs("(continue)\n");
  1016.          }
  1017.          if(strcmp(buf,".\n") == 0 || 
  1018.             strcmpi(buf,"***END\n") == 0 || 
  1019.             strcmpi(buf,"/EX\n") == 0)
  1020.             break;
  1021.          fprintf(fp,"%s",buf);
  1022.       }
  1023.  
  1024.       if (Psig != NULLCHAR) {
  1025.          sprintf(buf,"%s",Psig);
  1026.          if ((idf = fopen(buf,READ_TEXT)) != NULLFILE ) {
  1027.             while(fgets(buf,NNTPMAXLEN,idf) != NULL)
  1028.                fprintf(fp,"%s",buf);
  1029.             fclose(idf);
  1030.          }
  1031.       }
  1032.  
  1033. loop:
  1034.       fprintf(fp,"\n.\n");
  1035.  
  1036.       fclose(fp);
  1037.       fclose(fq);
  1038.  
  1039.       tprintf("Post another? ");
  1040.       recvline(sp->input,buf,NNTPMAXLEN);
  1041.       if (tolower(buf[0]) == 'n')
  1042.          goto done;
  1043.    }
  1044.  
  1045. done:
  1046.    freesession(sp);
  1047.    return 0;
  1048. }
  1049.  
  1050. /* checks for not valid chars in a line
  1051.  * returncode: 0 if valid; 1 if invalid */
  1052. static int check_blank(bp)
  1053. char *bp;
  1054. {
  1055.    if (strpbrk(bp,"!@#$%^&*()_+=<>,./?~`[]{}\|0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") == NULL)
  1056.       return 1;
  1057.    return 0;
  1058. }
  1059.  
  1060. /* list messages wating to be sent */
  1061. static int donntplist(argc,argv,p)
  1062. int argc;
  1063. char *argv[];
  1064. void *p;
  1065. {
  1066.    char tstring[80], line[20],
  1067.         group[LINELEN], *cp;
  1068.    struct stat stbuf;
  1069.    struct tm *tminfo, *localtime();
  1070.    FILE *fp;
  1071.  
  1072.    Current->flowmode = 1;          /* Enable the more mechanism */
  1073.    tprintf("    Job    Size  Date  Time Group\n");
  1074.    filedir(Newsqueue,0,line);
  1075.    while(line[0] != '\0') {
  1076.       sprintf(tstring,"%s/%s",Newsdir,line);
  1077.       if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
  1078.          tprintf("Can't open %s: %s\n",tstring,sys_errlist[errno]);
  1079.          continue;
  1080.       }
  1081.       if ((cp = strrchr(line,'.')) != NULLCHAR)
  1082.          *cp = '\0';
  1083.       sprintf(tstring,"%s/%s.txt",Newsdir,line);
  1084.       stat(tstring,&stbuf);
  1085.       tminfo = localtime(&stbuf.st_ctime);
  1086.       fgets(group,sizeof(group),fp);
  1087.       rip(group);
  1088.       tprintf("%7s %7ld %02d/%02d %02d:%02d %-25s\n",
  1089.          line, stbuf.st_size,
  1090.          tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour,
  1091.          tminfo->tm_min, group);
  1092.       (void) fclose(fp);
  1093.       pwait(NULL);
  1094.       filedir(Newsqueue,1,line);
  1095.    }
  1096.    Current->flowmode = 0;
  1097.    return 0;
  1098. }
  1099.  
  1100. /* kill a job in the mqueue */
  1101. static int donntpkill(argc,argv,p)
  1102. int argc;
  1103. char *argv[];
  1104. void *p;
  1105. {
  1106.    char s[LINELEN];
  1107.    char *cp;
  1108.  
  1109.    sprintf(s,"%s/%s.wrk",Newsdir,argv[1]);
  1110.    cp = strrchr(s,'.');
  1111.    if (remove(s))
  1112.       tprintf("Job id %s not found\n",argv[1]);
  1113.    strcpy(cp,".txt");
  1114.    (void) remove(s);
  1115.    return 0;
  1116. }
  1117.  
  1118.